/**
 * Copyright (c) 2011 eelpo.com
 * All rights reserved.
 */
package com.eelpo.cmmp.core.spring.orm.jpa;

import com.eelpo.common.util.BlankUtils;
import com.eelpo.common.util.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;

/**
 * <pre>
 * 
 * 多持久化配置管理类。
 * 
 * 处理持久化文件合并(同样配置采取覆盖操作)，分严格模式与非严格模式。
 * 
 * 严格模式仅仅合并持久化单元名称相同的配置，
 * 
 * 非严格模式以指定单元名称作为模板将其他以该单元名称作为前缀的配置合并。
 * 
 * 默认以非严格模式合并配置文件
 * 
 * </pre>
 * 
 * @author <a href="mailto:Brian@eelpo.com">汪洪州</a>
 * @since 1.0
 */
public class MultiConfigAwarePersistenceUnitManager extends DefaultPersistenceUnitManager {

	private static final Logger logger = LoggerFactory.getLogger(MultiConfigAwarePersistenceUnitManager.class);

	/**
	 * 是否严格模式
	 */
	private boolean strict = false;

	/**
	 * 持久化单元名称，非严格模式下并且作为合并配置的前缀,注意非严格模式下先配置模板路径
	 */
	private String persistenceUnitName;

	public void setStrict(boolean strict) {
		this.strict = strict;
	}

	public void setPersistenceUnitName(String persistenceUnitName) {
		this.persistenceUnitName = persistenceUnitName;
	}

	@Override
	public void setPersistenceXmlLocation(String persistenceXmlLocation) {
		String[] persistenceXmlLocations = null;
		persistenceXmlLocation = persistenceXmlLocation.trim();
		if (StringUtils.isContains(persistenceXmlLocation, ",")) {
			persistenceXmlLocations = persistenceXmlLocation.split(",");
		} else if (StringUtils.isContains(persistenceXmlLocation, " ")) {
			persistenceXmlLocations = persistenceXmlLocation.split(" ");
		} else {
			persistenceXmlLocations = new String[] { persistenceXmlLocation };
		}
		super.setPersistenceXmlLocations(persistenceXmlLocations);
	}

	/**
	 * 判断该持久化单元是否作为模板
	 * 
	 * @param pui
	 *            持久化单元
	 * @return 返回是否持久化单元的模板配置
	 */
	protected boolean isTemplatePersistenceUnit(MutablePersistenceUnitInfo pui) {
		return (!strict && persistenceUnitName.equals(pui.getPersistenceUnitName()));
	}

	/**
	 * 严格模式下获取同名的已经存在的配置，非严格模式下获取模板配置
	 * 
	 * @param pui
	 *            持久化单元
	 * @return 返回持久化单元
	 */
	protected MutablePersistenceUnitInfo getMainPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
		if (strict) {
			return getPersistenceUnitInfo(pui.getPersistenceUnitName());
		} else if (pui.getPersistenceUnitName().startsWith(persistenceUnitName)) {

			final MutablePersistenceUnitInfo result = getPersistenceUnitInfo(persistenceUnitName);

			if (BlankUtils.isBlank(result)) {
				throw new IllegalStateException("No persistence unit found with name [" + persistenceUnitName + "] ");
			}

			return result;
		}
		return null;
	}

	@Override
	protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
		super.postProcessPersistenceUnitInfo(pui);

		if (isTemplatePersistenceUnit(pui)) {
			logger.info("Find persistence unit template [" + pui.getPersistenceUnitName() + "]");
			return;
		}

		final MutablePersistenceUnitInfo mainPui = getMainPersistenceUnitInfo(pui);

		if (strict) {
			pui.addJarFileUrl(pui.getPersistenceUnitRootUrl());
		}

		if (!BlankUtils.isBlank(mainPui)) {
			if (strict) {
				mergePersistenceUnit(mainPui, pui);
			} else {
				mergePersistenceUnit(pui, mainPui);
			}
		} else if (!pui.getPersistenceUnitName().equals(persistenceUnitName)) {
			logger.debug("Persistence unit [" + pui.getPersistenceUnitName() + "] Skipping....");
		}
	}

	/**
	 * 合并持久化单元配置
	 * 
	 * @param from
	 *            源持久化配置
	 * @param to
	 *            目标持久化配置
	 */
	protected void mergePersistenceUnit(MutablePersistenceUnitInfo from, MutablePersistenceUnitInfo to) {

		if (!BlankUtils.isBlank(from.getJtaDataSource())) {
			if (!BlankUtils.isBlank(to.getJtaDataSource())) {
				logger.debug("Persistence unit [{}] <jta-data-source> was override by [{}].", to.getPersistenceUnitName(), from.getPersistenceUnitName());
			}
			to.setJtaDataSource(from.getJtaDataSource());
		}

		if (!BlankUtils.isBlank(from.getNonJtaDataSource())) {
			if (!BlankUtils.isBlank(to.getNonJtaDataSource())) {
				logger.debug("Persistence unit [{}] <non-jta-data-source> was override by [{}].", to.getPersistenceUnitName(), from.getPersistenceUnitName());
			}
			to.setNonJtaDataSource(from.getNonJtaDataSource());
		}

		if (!BlankUtils.isBlank(from.getPersistenceProviderClassName())) {
			if (!BlankUtils.isBlank(to.getPersistenceProviderClassName())) {
				logger.debug("Persistence unit [{}] <provider> was override by [{}].", to.getPersistenceUnitName(), from.getPersistenceUnitName());
			}
			to.setPersistenceProviderClassName(from.getPersistenceProviderClassName());
			to.setPersistenceProviderPackageName(from.getPersistenceProviderPackageName());
		}

		if (!BlankUtils.isBlank(from.getMappingFileNames())) {
			to.getMappingFileNames().removeAll(from.getMappingFileNames());
			to.getMappingFileNames().addAll(from.getMappingFileNames());
		}

		if (!BlankUtils.isBlank(from.getJarFileUrls())) {
			to.getJarFileUrls().removeAll(from.getJarFileUrls());
			to.getJarFileUrls().addAll(from.getJarFileUrls());
		}

		if (!BlankUtils.isBlank(from.getManagedClassNames())) {
			to.getManagedClassNames().removeAll(from.getManagedClassNames());
			to.getManagedClassNames().addAll(from.getManagedClassNames());
		}

		if (!BlankUtils.isBlank(from.getProperties())) {
			for (Object key : from.getProperties().keySet()) {
				to.getProperties().put(key, from.getProperties().get(key));
			}
		}
	}
}
